//
// ThreadPool.cpp for  in /home/herpec_j/Dropbox/Projets/raytracer-2016/Sources/DesignPattern/ThreadPool/
//
// Made by Jonathan
// Login   <ledey_l@epitech.net>
//
// Started on  Fri Sep 20 14:07:58 2013 Jonathan
// Last update Fri Sep 20 23:56:51 2013 Jonathan
//

#include		<unistd.h>

#include		"DesignPattern/ThreadPool/ThreadPool.hpp"

namespace		DesignPattern
{
  namespace		ThreadPool
  {
    ThreadPool::ThreadPool(void)
    {
      mPoolSize = sysconf(_SC_NPROCESSORS_ONLN);
      InitializeThreadpool();
    }

    ThreadPool::ThreadPool(unsigned short rPoolSize) : mPoolSize(rPoolSize)
    {
      InitializeThreadpool();
    }

    void		*ThreadPool::StartThread(void *rArg)
    {
      DesignPattern::ThreadPool::ThreadPool	*thread_pool =
	(DesignPattern::ThreadPool::ThreadPool *) rArg;

      thread_pool->ExecuteThread();
      return (NULL);
    }

    void		ThreadPool::InitializeThreadpool(void)
    {
      pthread_t		thread_id;

      mIsPoolStarted = true;
      for (unsigned short index = 0;
	   index < mPoolSize;
	   ++index)
	{
	  ThreadInfo	*thread_info = new ThreadInfo;

	  thread_info->mPoolSize = mPoolSize;
	  thread_info->mThreadNumber = index;
	  pthread_create(&thread_id, NULL, StartThread, (void *) this);
	  mThreads.push_back(thread_id);
	  mThreadInfos.push_back(thread_info);
	}
    }

    void		ThreadPool::AddTask(Task *rTask)
    {
      mTaskMutex.Lock();
      mTasks.push_back(rTask);
      mTaskCondVar.Signal();
      mTaskMutex.Unlock();
    }

    void		*ThreadPool::ExecuteThread(void)
    {
      Task		*task = NULL;

      while (true)
	{
	  mTaskMutex.Lock();
	  while (mIsPoolStarted && mTasks.empty())
	    {
	      mTaskCondVar.Wait(mTaskMutex.GetMutex());
	    }
	  if (!mIsPoolStarted)
	    {
	      mTaskMutex.Unlock();
	      pthread_exit(NULL);
	    }
	  task = mTasks.front();
	  mTasks.pop_front();
	  mTaskMutex.Unlock();
	  task->Run();
	  delete task;
	}
    }

    void		ThreadPool::DestroyThreadpool(void)
    {
      void		*result = NULL;

      mTaskMutex.Lock();
      mIsPoolStarted = false;
      mTaskMutex.Unlock();
      mTaskCondVar.Broadcast();
      for (unsigned short index = 0;
	   index < mPoolSize;
	   ++index)
	{
	  pthread_join(mThreads[index], &result);
	  mTaskCondVar.Broadcast();
	}
    }

    ThreadPool::~ThreadPool(void)
    {
      if (mIsPoolStarted)
	{
	  DestroyThreadpool();
	}
    }
  };
};
